package cl.uchile.sketchpad.web.controllers;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import static org.springframework.web.bind.annotation.RequestMethod.*;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import cl.uchile.sketchpad.persistance.model.Activity;
import cl.uchile.sketchpad.persistance.model.Group;
import cl.uchile.sketchpad.persistance.model.Slide;
import cl.uchile.sketchpad.persistance.model.SubjectVersion;
import cl.uchile.sketchpad.persistance.model.User;
import cl.uchile.sketchpad.services.ActivityService;
import cl.uchile.sketchpad.services.GroupService;
import cl.uchile.sketchpad.services.SlideService;
import cl.uchile.sketchpad.services.SubjectVersionService;
import cl.uchile.sketchpad.services.UserService;
import cl.uchile.sketchpad.web.controllers.util.HttpUtil;
import cl.uchile.sketchpad.web.controllers.util.SktUtil;

/**
 * Handles requests for the application home page.
 */
@Controller
public class SketchpadController {

	private static final Logger LOG = LoggerFactory.getLogger(SketchpadController.class);
	
	@Autowired private UserService userService;
	@Autowired private SubjectVersionService subjectVersionService;
	@Autowired private ActivityService activityService;
	@Autowired private SlideService slideService;
	@Autowired private GroupService groupService;
	
	@Transactional(readOnly = true)
	@RequestMapping(value="/subjectMembers/{subjectVersionId}.json", produces = "application/json", method = GET)
	public @ResponseBody Set<User> getSubjectMembers(
			HttpServletRequest request,
			@PathVariable Integer subjectVersionId
	) {
		User user = this.userService.getUser(request);
		if ( user == null || user.getId() == null )
			return null;
		
		SubjectVersion subjectVersion = null;
		if ( subjectVersionId == null 
				|| (subjectVersion = this.subjectVersionService.findById(subjectVersionId)) == null ) {
			String desc = "Requested subjectMembers with invalid subjectVersionId: " + subjectVersionId;
			LOG.error(desc);
			System.err.println(desc);
			return new HashSet<User>();
		}
		
		Set<User> res = subjectVersion.getUsers();
		
		// Lazy intialization para que spring no se caiga
		for ( User us : res ) {
			us.getSubjects();
			us.getSubjectVersions();
		}
		
		return res;
	}
	
	
	@Transactional
	private void setupApp(
			HttpServletRequest request,
			Model model,
			Integer subjectVersionId,
			Integer activityId,
			String groupName,
			String groupType 
	) {
		model.addAttribute("cosServer", HttpUtil.getHostName(request) + "/CoupledObjectWebServer");
		
		User user = this.userService.getUser(request);
		model.addAttribute("user", user);
			
		if ( subjectVersionId != null ) {
			SubjectVersion subjectVersion = subjectVersionService.findById(subjectVersionId);
			model.addAttribute("subjectVersion", subjectVersion);
			
			if ( subjectVersion != null )
				model.addAttribute("subjectVersionUsers", new LinkedHashSet<User>(subjectVersion.getUsers()));
			else
				LOG.warn("Recibido request con subjectVersionId invalido: " + subjectVersionId);
		}
		
		if ( activityId != null ) {
			Activity activity = this.activityService.findById(activityId);
			
			if ( activity == null )
				LOG.warn("Recibido request con activityId invalido: " + activityId);
			
			model.addAttribute("activity",activity);
		}
		
		model.addAttribute("tvs", new LinkedList<User>(this.userService.getTVs()));
		
		HashMap<String,String> group = new HashMap<String, String>(2);
		group.put("name",groupName);
		group.put("type",groupType);
		model.addAttribute("group", group);	
		
	}
	
	@Transactional
	@RequestMapping( value = "/uploadSlides/{subjectVersionId}/{activityId}", method = { GET } )
	public String uploadSlides(
			HttpServletRequest request,
			Model model,
			@PathVariable Integer subjectVersionId,
			@PathVariable Integer activityId
	) {
		this.setupApp(request, model, subjectVersionId, activityId, null, null);
		return "uploadSlides";
	}
	

	@Transactional
	@RequestMapping( value = "/view/{username}", method = { GET,  POST } )
	public String view(
			HttpServletRequest request,
			HttpServletResponse response,
			Model model,
			@PathVariable String username,
			@RequestParam(value="subject_version_id", required=false) Integer subjectVersionId,
			@RequestParam(value="activity_id", required=false) Integer activityId,
			@RequestParam(value="group_name", required=false) String groupName,
			@RequestParam(value="group_type", required=false) String groupType
	) {
		
		User user = this.userService.findByUsername(username);		
		if ( user == null || !SktUtil.isMonitor(user) ) {
			try {
				response.sendError(403, "Invalid monitor account name");
			} catch (IOException e) { }
		} else {
			
			HttpSession session = request.getSession(true);
			session.setMaxInactiveInterval(-1);
			session.setAttribute("user", user);
			
			this.setupApp(request, model, subjectVersionId, activityId, groupName, groupType);
			
			model.addAttribute("forward","view/" + username);
			model.addAttribute("mode", "app");
			return "appNew";
		}
		
		return null;
	}


	@Transactional
	@RequestMapping( value = "/appOld/{subjectVersionId}/{activityId}", method = { GET, POST } )
	public String appOld( 
			HttpServletRequest request,
			Model model,
			@PathVariable Integer subjectVersionId,
			@PathVariable Integer activityId,
			@RequestParam(value="group_name", required=false) String groupName,
			@RequestParam(value="group_type", required=false) String groupType
	) {
		this.setupApp(request, model, subjectVersionId, activityId, groupName, groupType);
		model.addAttribute("forward","app/" + subjectVersionId + "/" + activityId);		
		model.addAttribute("mode", "app");
		return "app";
	}

	@Transactional
	@RequestMapping( value = "/app/{subjectVersionId}/{activityId}", method = { GET } )
	public String app( 
			HttpServletRequest request,
			Model model,
			@PathVariable Integer subjectVersionId,
			@PathVariable Integer activityId,
			@RequestParam(value="group_name", required=false) String groupName,
			@RequestParam(value="group_type", required=false) String groupType
	) {
		this.setupApp(request, model, subjectVersionId, activityId, groupName, groupType);
		return "appNew";
	}
	
	@Transactional
	@RequestMapping(value="/app/newGroup", produces = "application/json", method = {POST})
	public @ResponseBody Group newGroup(
			HttpServletRequest request,
			@RequestParam(value="activityId") Integer activityId,
			@RequestParam(value="name", required=true) String name,
			@RequestParam(value="type", required=true) String type
	) {
		User user = this.userService.getUser(request);
		if ( user == null || user.getId() == null )
			return null;
		
		Group res = new Group();
		res.setAuthor(user);
		
		if ( name == null || "".equals(name.trim()) ) {
			String error = "recieved a request to create a new group without name";
			System.err.println(error);
			LOG.error(error);
			return null;
		}
		res.setName(name);
		
		if ( type == null || !SktUtil.isValidGroupType(type) ) {
			String error = "recieved a request to create a new group with invalid group type";
			System.err.println(error);
			LOG.error(error);
			return null;
		}
		res.setType(type);
		
		Activity activity = null;
		if ( activityId == null 
				|| (activity = this.activityService.findById(activityId)) == null ) {
			String error = "recieved a request to create a new group with invalid subjectVersionId";
			System.err.println(error);
			LOG.error(error);
			return null;
		}
		res.setActivity(activity);
		
		try {
			this.groupService.persist(res);			
		} catch ( Exception e ) {
			e.printStackTrace(System.err);
			LOG.error(e.getLocalizedMessage(), e);
			return null;
		}
		
		return res;
	}
	
	@Transactional
	@RequestMapping( value="/app/newSlide", produces = "application/json",  method = {POST} )
	public @ResponseBody Slide newSlide(
			HttpServletRequest request,
			@RequestParam(value="activity_id", required=true) Integer activityId,
			@RequestParam(value="type", required=true) String type,
			@RequestParam(value="group_id", required=false) Integer groupId
	) {
		User user = this.userService.getUser(request);
		if ( user == null || user.getId() == null )
			return null;

		Slide res = new Slide();

		res.setAuthor(user);
		res.setCreated(new Date());
		
		if ( Slide.Type.GROUP.getValue().equals(type) && groupId == null ) {
			String error = "recieved a request to create a new group slide without providing group_id";
			System.err.println(error);
			LOG.error(error);
			return null;
		}
		
		if ( groupId != null ) {
			Group group = this.groupService.findById(groupId);
			if ( group == null ) {
				String error = "recieved a request to create a new group slide with invalid group_id";
				System.err.println(error);
				LOG.error(error);
				return null;
			}
			res.setGroup(group);
		}
		
		
		if ( !SktUtil.isValidSlideType(type) ) {
			String error = "recieved a request to create a new slide with invalid type";
			System.err.println(error);
			LOG.error(error);
			return null;
		}
		res.setType(type);
		
		Activity activity = this.activityService.findById(activityId);
		if ( activity == null ) {
			String error = "recieved a request to create a new slide with invalid activity_id";
			System.err.println(error);
			LOG.error(error);
			return null;
		}
		res.setActivity(activity);
		
		if ( groupId != null ) {
			Group group = this.groupService.findById(groupId);
			if ( group == null &&  Slide.Type.GROUP.getValue().equals(type) ) {
				String error = "recieved a request to create a new group slide with invalid group_id";
				System.err.println(error);
				LOG.error(error);
				return null;
			}
			res.setGroup(group);
		}
				
		try {
			this.slideService.persist(res);			
		} catch ( Exception e ) {
			e.printStackTrace(System.err);
			LOG.error(e.getLocalizedMessage(), e);
			return null;
		}
		
		return res;
	}

	
}
